﻿/*
 * Author: Mattan360
 * Contributors: VillageIdiot
 */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using Irrlicht.Net;
using Irrlicht.Net.Core;
using Irrlicht.Net.GUI;
using Irrlicht.Net.IO;
using Irrlicht.Net.Scene;
using Irrlicht.Net.Video;

namespace Irrlicht.Net
{
    public class EventReceiver
    {
        [DllImport(@"IrrlichtW.dll", CallingConvention = CallingConvention.Cdecl)]
        internal static extern bool initCallBack(IntPtr eventcb);
     
        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        public delegate bool OnEventCallback(IntPtr e);

        // A delegate type for hooking up IrrEvent notifications.
        public delegate bool IrrEventHandler(IrrEventArgs e);

        public event IrrEventHandler IrrEvent;

        static GCHandle gch;

        public static OnEventCallback myCallBack;
        public EventReceiver()
        {
            myCallBack = new OnEventCallback(OnEvent);
            gch = GCHandle.Alloc(myCallBack);
            initCallBack(Marshal.GetFunctionPointerForDelegate(myCallBack));
        }

        /// <summary>
        /// Event handler which is called by the native code.
        /// </summary>
        /// <param name="e">Pointer to IrrEventArgs structure.</param>
        /// <returns>true if event processing will stop.</returns>
        public bool OnEvent(IntPtr e)
        {
            bool stopProcessing = false;

            if (IrrEvent != null)
            {
                IrrEventRaw myEventRaw = (IrrEventRaw)Marshal.PtrToStructure(e, typeof(IrrEventRaw));
                IrrEventArgs myEvent = ConvertRawEventData(myEventRaw);

                if (myEvent != null)
                {
                    stopProcessing = IrrEvent(myEvent);
                }
            }

            return stopProcessing;
        }

        internal IrrEventArgs ConvertRawEventData(IrrEventRaw e)
        {
            IrrEventArgs myEvent = new IrrEventArgs();
            myEvent.EventType = e.EventType;

            switch (myEvent.EventType)
            {
                case EventTypes.GUI:
                    {
                        myEvent.GUIEvent.Caller = new Element(e.Caller);
                        myEvent.GUIEvent.Element = new Element(e.Element);
                        myEvent.GUIEvent.GUIEventType = e.GUIEventType;
                        break;
                    }
                case EventTypes.JoystickInput:
                    {
                        myEvent.JoystickEvent.ButtonStates = e.ButtonStates;
                        myEvent.JoystickEvent.Axis = new short[(int) JoystickEvent.JoystickAxes.Count];
                        myEvent.JoystickEvent.Axis[0] = e.AxisX;
                        myEvent.JoystickEvent.Axis[1] = e.AxisY;
                        myEvent.JoystickEvent.Axis[2] = e.AxisZ;
                        myEvent.JoystickEvent.Axis[3] = e.AxisT;
                        myEvent.JoystickEvent.Axis[4] = e.AxisU;
                        myEvent.JoystickEvent.Axis[5] = e.AxisV;
                        myEvent.JoystickEvent.POV = e.POV;
                        myEvent.JoystickEvent.Joystick = e.Joystick;
                        break;
                    }
                case EventTypes.KeyInput:
                    {
                        myEvent.KeyInput.Char = e.Char;
                        myEvent.KeyInput.Key = e.Key;
                        myEvent.KeyInput.KeyModifiers = e.KeyModifiers;
                        break;
                    }
                case EventTypes.LogText:
                    {
                        myEvent.LogEvent.Text = Marshal.PtrToStringAnsi(e.LogTextPtr);
                        myEvent.LogEvent.Level = e.LogLevel;
                        break;
                    }
                case EventTypes.MouseInput:
                    {
                        myEvent.MouseInput.X = e.MouseX;
                        myEvent.MouseInput.Y = e.MouseY;
                        myEvent.MouseInput.Wheel = e.MouseWheel;
                        myEvent.MouseInput.Modifiers = e.MouseModifiers;
                        myEvent.MouseInput.ButtonStates = e.MouseButtonStates;
                        myEvent.MouseInput.Event = e.MouseEvent; 
                        break;
                    }
                case EventTypes.User:
                    {
                        myEvent.UserEvent.UserData1 = e.UserData1;
                        myEvent.UserEvent.UserData2 = e.UserData2;
                        break;
                    }
                default:
                    {
                        return null;
                    }
            }

            return myEvent;
        }

        void Dispose()
        {
            initCallBack(IntPtr.Zero);
            gch.Free();
        }
    }
}
